package com.zhongwang.mms.config.shiro;

import at.pollux.thymeleaf.shiro.dialect.ShiroDialect;
import com.zhongwang.mms.config.shiro.filter.KickOutSessionFilter;
import com.zhongwang.mms.config.shiro.filter.LogoutFilter;
import com.zhongwang.mms.config.shiro.realm.ShiroRealm;
import com.zhongwang.mms.constant.Constant;
import net.sf.ehcache.CacheManager;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.cache.ehcache.EhCacheManager;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.web.servlet.Cookie;
import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import javax.servlet.Filter;
import java.util.LinkedHashMap;
import java.util.Map;

/**
 * @Description: 权限框架 shiro 配置
 *
 * @author: China.sgl
 * @Date: 2019/9/10 09:22
 */
@Configuration
public class ShiroConfig {

    /** Session 超时时间，单位为毫秒（默认30分钟） */
    @Value("${shiro.session.expireTime:30}")
    private int expireTime;

    /** 相隔多久检查一次session的有效性，单位毫秒，默认就是10分钟 */
    @Value("${shiro.session.validationInterval:10}")
    private int validationInterval;

    /** 同一个用户最大会话数 */
    @Value("${shiro.session.maxSession:-1}")
    private int maxSession;

    /** 踢出 之前 / 之后 登录的用户，默认踢出之前登录的用户 */
    @Value("${shiro.session.kickOutAfter:false}")
    private boolean kickOutAfter;

    /** 登录地址 */
    @Value("${shiro.user.loginUrl:/}")
    private String loginUrl;

    /** 权限认证失败地址 */
    @Value("${shiro.user.unauthorizedUrl:/403}")
    private String unauthorizedUrl;

    /**
     * 密码加密验证器
     */
    @Bean
    public HashedCredentialsMatcher hashedCredentialsMatcher() {
        HashedCredentialsMatcher credentialsMatcher = new HashedCredentialsMatcher();
        credentialsMatcher.setHashAlgorithmName("MD5");
        credentialsMatcher.setHashIterations(8);
        credentialsMatcher.setStoredCredentialsHexEncoded(true);
        return credentialsMatcher;
    }

    /**
     * 缓存管理器 使用Ehcache实现
     */
    @Bean
    public EhCacheManager ehCacheManager() {
        CacheManager cacheManager = CacheManager.getCacheManager("zhongwang");
        EhCacheManager em = new EhCacheManager();
        if (cacheManager == null) {
            em.setCacheManagerConfigFile("classpath:ehcache-shiro.xml");
        } else {
            em.setCacheManager(cacheManager);
        }
        return em;
    }

    /**
     * 自定义Realm
     */
    @Bean
    public ShiroRealm shiroRealm(HashedCredentialsMatcher hashedCredentialsMatcher) {
        ShiroRealm shiroRealm = new ShiroRealm();
        // 密码加密器
        shiroRealm.setCredentialsMatcher(hashedCredentialsMatcher);
        // 开启缓存
        shiroRealm.setCachingEnabled(true);
        // 启用授权缓存，即缓存AuthorizationInfo信息，默认false
        shiroRealm.setAuthorizationCachingEnabled(true);
        // 缓存AuthorizationInfo信息的缓存名称  在ehcache-shiro.xml中有对应缓存的配置
        shiroRealm.setAuthorizationCacheName(Constant.AUTHORIZATION_CACHE);
        return shiroRealm;
    }

    /**
     * 会话管理器
     */
    @Bean
    public DefaultWebSessionManager sessionManager() {
        DefaultWebSessionManager manager = new DefaultWebSessionManager();
        // 删除过期的session
        manager.setDeleteInvalidSessions(true);
        // 设置全局session超时时间
        manager.setGlobalSessionTimeout(expireTime * 60 * 1000);
        // 去掉 JSESSIONID
        manager.setSessionIdUrlRewritingEnabled(false);
        // 是否定时检查session
        manager.setSessionValidationSchedulerEnabled(true);
        // 给JSESSIONID自定义名称 这样多个系统在同一个服务器采用不同端口号或者项目名想的SESSION不会污染。 ---徐一贺 2021-01-30
        Cookie cookie = manager.getSessionIdCookie();
        cookie.setName("mms");
        return manager;
    }

    /**
     * 安全管理器
     */
    @Bean
    public SecurityManager securityManager(ShiroRealm shiroRealm) {
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        // 注入缓存管理器;
        securityManager.setCacheManager(ehCacheManager());
        // 设置realm.
        securityManager.setRealm(shiroRealm);
        // session管理器
        securityManager.setSessionManager(sessionManager());
        return securityManager;
    }



    /**
     * Shiro过滤器配置
     */
    @Bean
    public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager) {
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
        // Shiro的核心安全接口,这个属性是必须的
        shiroFilterFactoryBean.setSecurityManager(securityManager);
        // 身份认证失败，则跳转到登录页面的配置
        shiroFilterFactoryBean.setLoginUrl(loginUrl);
        // 权限认证失败，则跳转到指定页面
        shiroFilterFactoryBean.setUnauthorizedUrl(unauthorizedUrl);
        // Shiro连接约束配置，即过滤链的定义
        LinkedHashMap<String, String> filterChainDefinitionMap = new LinkedHashMap<>();
        // 对静态资源设置匿名访问
        filterChainDefinitionMap.put("/adminLTE/**", "anon");
        filterChainDefinitionMap.put("/css/**", "anon");
        filterChainDefinitionMap.put("/img/**", "anon");
        filterChainDefinitionMap.put("/js/**", "anon");
        filterChainDefinitionMap.put("/plugin/**", "anon");
        // 退出 logout地址，shiro去清除session
        filterChainDefinitionMap.put("/logout", "logout");
        // 不需要拦截的访问
        filterChainDefinitionMap.put("/login", "anon");

        Map<String, Filter> filters = new LinkedHashMap<>();
        filters.put("kickOut", kickOutSessionFilter());
        // 注销成功，则跳转到指定页面
        filters.put("logout", logoutFilter());
        shiroFilterFactoryBean.setFilters(filters);

        // 所有请求需要认证
        filterChainDefinitionMap.put("/**", "authc,kickOut");
        shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);

        return shiroFilterFactoryBean;
    }


    /**
     * 同一个用户多设备登录限制
     */
    private KickOutSessionFilter kickOutSessionFilter() {
        KickOutSessionFilter kickOutSessionFilter = new KickOutSessionFilter();
        kickOutSessionFilter.setCacheManager(ehCacheManager());
        kickOutSessionFilter.setSessionManager(sessionManager());
        // 同一个用户最大的会话数，默认-1无限制；比如2的意思是同一个用户允许最多同时两个人登录
        kickOutSessionFilter.setMaxSession(maxSession);
        // 是否踢出后来登录的，默认是false；即后者登录的用户踢出前者登录的用户；踢出顺序
        kickOutSessionFilter.setKickOutAfter(kickOutAfter);
        // 被踢出后重定向到的地址；
        kickOutSessionFilter.setKickOutUrl("/?kickOut=1");
        return kickOutSessionFilter;
    }

    /**
     * 退出过滤器
     */
    private LogoutFilter logoutFilter() {
        LogoutFilter logoutFilter = new LogoutFilter();
        logoutFilter.setCacheManager(ehCacheManager());
        logoutFilter.setLoginUrl(loginUrl);
        return logoutFilter;
    }

    /**
     * thymeleaf模板引擎和shiro框架的整合
     */
    @Bean
    public ShiroDialect shiroDialect() {
        return new ShiroDialect();
    }

    /**
     * 开启Shiro注解通知器
     */
    @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(@Qualifier("securityManager") SecurityManager securityManager) {
        AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
        authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
        return authorizationAttributeSourceAdvisor;
    }
}
